#include <errno.h>
#include <string.h>

#include "CPU.h"
#include "base/Configuration.h"
#include "base/Simulator.h"
#include "base/isa_ext.h"
#include "base/signals/SigHandler.h"
#include "base/signals/SigManager.h"

#if defined(__linux__) || defined(_AIX)
const char *sys_signame[NSIG] = {
    "",
    "HUP",
    "INT",
    "QUIT",
    "ILL",
    "TRAP",
    "ABRT",
    "BUS",
    "FPE",
    "KILL",
    "USR1",
    "SEGV",
    "USR2",
    "PIPE",
    "ALRM",
    "TERM",
    "STKFLT",
    "CHLD",
    "CONT",
    "STOP",
    "TSTP",
    "TTIN",
    "TTOU",
    "URG",
    "XCPU",
    "XFSZ",
    "VTALRM",
    "PROF",
    "WINCH",
    "IO",
    "PWR",
    "SYS",
};
#endif

const char *sim_signame[SIM_NSIG] = {
    "ROLLBACK",
    "LOGABORT",
    "WATCH",
    "SOW",
};

const char* getSigName(int signum) {
    if (signum > 0 && signum < NSIG) {
        return sys_signame[signum];
    } else if (signum >= SIM_SIGSTART && signum < SIM_SIGSTART+SIM_NSIG) {
        return sim_signame[signum - SIM_SIGSTART];
    } else {
        return "<unknown signal>";
    }
}

sigjmp_buf sigjmpEnv;   // referred in cpu.cpp and SigHandler.cpp

NEW_DELETE_OPERATORS_CPP(SigManager,1);

Logger* signalLog = NULL;

SigManager::SigManager () {
    if (!signalLog) {
        signalLog = NEW Logger("signalLog", "sig", "none", "signal handling log");
    }

    signal_alarm_frequency = global_cfg->getIntegerArgument("signal_alarm_frequency",
                                                            "frequency to deliver SIGALRM",
                                                            1000,
                                                            CONFIG_MIN,
                                                            1);
    signal_alternate_stack.ss_sp    = CALLOC(1, SIGNAL_STACK_SIZE);
    signal_alternate_stack.ss_size  = SIGNAL_STACK_SIZE;
    signal_alternate_stack.ss_flags = 0;
    int result = sigaltstack(&signal_alternate_stack, NULL);
    if (result != 0) {
        Log->e("[SigManager] sigaltstack failed\n");
    }

    schedulingHead = 0;
    procSchedulingHead = 0;
    numRegisteredHandlers = 0;

    for (int i=0; i<MAX_SIG_NUM; i++) {
        handlers[i] = NULL;
    }

    // register the defaultSigSegvHandler for the better debugging
    struct sigaction action;
    action.sa_sigaction = (SIGNAL_ACTION)defaultSigSegvHandler;
    sigfillset(&action.sa_mask);
    action.sa_flags = SA_RESTART | SA_SIGINFO;
    if (sigaction(SIGSEGV, &action, NULL)) {
        signalLog->i("[SigManager] failed to register defaultSigSegvHandler\n");
    }
}

SigManager::~SigManager () {

    FREE(signal_alternate_stack.ss_sp);

    for (int i = 0; i < MAX_SIG_NUM; i++) {
        if (handlers[i]) {
            FREE(handlers[i]);
        }
    }
    for (int i = 0; i < MAX_NUM_CPUS; i++) {
        if (procRecords[i].sigContext != NULL) {
            ASSERT(procRecords[i].scheduled == true);
            delete procRecords[i].sigContext;
        }
    }
    if (signalLog) delete signalLog;
}

// ----------------------------------
// public functions from here.
// ----------------------------------

int SigManager::registerHandler (int signum, const struct sigaction *act, struct sigaction* oldact) {
    if (!(0 < signum && signum <= MAX_SIG_NUM)) {
        errno = EINVAL;
        return -1;
    }

    if (oldact != NULL) {
        if (handlers[signum]) {
            memcpy(oldact, handlers[signum], sizeof(struct sigaction));  
        } else {
            oldact->sa_handler = SIG_DFL;
        }
    }

    if (act != NULL) {
        // first time registration for this signum, allocate space
        if (handlers[signum] == NULL) {
            regd_signum[numRegisteredHandlers++] = signum;
            handlers[signum] = (struct sigaction*)CALLOC(1, sizeof(struct sigaction));
        }

        signalLog->i("[SigManager] registering handler function %p for SIG%s\n", act->sa_sigaction, getSigName(signum));
        // we have only tested SA_SIGINFO support
        // ASSERT((act->sa_flags & SA_SIGINFO) == SA_SIGINFO);
        // we do not support custom restorers since the simulator
        // implicitly does its own thing in ISA_EXT_SIGEXIT

        ASSERT((act->sa_flags & 0x04000000/*SA_RESTORER*/) == 0 ||
               act->sa_restorer == NULL);
        memcpy(handlers[signum], act, sizeof(struct sigaction));
        
        // don't pass simulator specific signals to underlying OS via sigaction call
        if (signum >= SIM_SIGSTART) {
            return 0;
        }
        
        struct sigaction action;
        action.sa_sigaction = (SIGNAL_ACTION)signalHandler;
        action.sa_flags = act->sa_flags | SA_SIGINFO | SA_ONSTACK;
        action.sa_mask = act->sa_mask;
        return sigaction(signum, &action, NULL);
    }

    return 0;
}

// called as part of goto_sim() to see whether application code registered signal handlers in real mode
void SigManager::checkHandlerRegistration () {
    sigset_t mask,omask;

    sigfillset(&mask);
    if (sigprocmask(SIG_SETMASK, &mask, &omask) != 0) {
        Log->e("[SigManager] fail to set a signal mask\n");
    }

    struct sigaction oaction;
    for (int i=1;i<NSIG;i++) {
        int result = sigaction(i, NULL, &oaction);
        // if error return just skip
        if (result != 0) {
            continue;
        }

        // special check for defaultSigSegvHandler
        if (i == SIGSEGV &&
            oaction.sa_sigaction == (SIGNAL_ACTION)defaultSigSegvHandler)
        {
            // application didn't supercede the defaultSigSegvHandler
            // let the handler be there so that we can catch segmentation fault in
            // simulator mode.
            continue;
        }

        if (oaction.sa_handler != SIG_DFL && oaction.sa_handler != SIG_IGN) {
            if (registerHandler(i, &oaction, NULL)) {
                Log->e("[SigManager] failed to register Simulator's signal handler for SIG%s\n", getSigName(i));
            }
            signalLog->i("[SigManager] Simulator sig handler for SIG%s is registered\n", getSigName(i));
        }
    }

    if (sigprocmask(SIG_SETMASK, &omask, NULL) != 0) {
        Log->e("[SigHandler] fail to unset a signal mask\n");
    }
}

// called by goto_real to unregister the simulator's signal handler
// and re-register application's signal handlers
void SigManager::restoreHandlerRegistration ()
{
    sigset_t mask,omask;

    sigfillset(&mask);
    if (sigprocmask(SIG_SETMASK, &mask, &omask) != 0) {
        Log->e("[SigManager] fail to set a signal mask\n");
    }

    for (int i=0;i<numRegisteredHandlers;i++) {
        int sn = regd_signum[i];
        if (sn < NSIG) {
            struct sigaction* hd = handlers[sn];
            if (sigaction(sn, hd, NULL)) {
                Log->e("[SigManager] failed to restore user's handler for SIG%s\n", getSigName(sn));
            }
            signalLog->i("[SigManager] the user's handler for SIG%s is restored\n", getSigName(sn));
            FREE(handlers[sn]);
            handlers[sn] = NULL;
        }
    }
    numRegisteredHandlers = 0;

    if (sigprocmask(SIG_SETMASK, &omask, NULL) != 0) {
        Log->e("[SigHandler] fail to unset a signal mask\n");
    }
}

// called by sigHandler
void* SigManager::getHandler (int signum) {
    // we shouldn't be running signals we haven't regd for
    // and we should have a handler for evertyhing we've reg'd for
    if (!handlers[signum]) {
        Log->e("[SigManager] getHandler called for unregistered signal SIG%s (%d).\n", getSigName(signum), signum);
    } 
    return (void*)handlers[signum]->sa_handler;
}

// called by sigHandler
sigset_t* SigManager::getSigMask (int signum) {
    // we shouldn't be running signals we haven't regd for
    // and we should have a handler for evertyhing we've reg'd for
    ASSERT(handlers[signum] != NULL);
    return &handlers[signum]->sa_mask;
}

void SigManager::checkSignal (int signum) {
    if (handlers[signum] == NULL) {
        Log->e("[SigManager] signal without a user handler was invoked (SIG%s)\n", getSigName(signum));
    }

    // keep count of how many times we have seen this signal.
    // only really used for SIGALRM but interesting to keep for all signals.
    records[signum].occurances++;
}

void SigManager::scheduleSignal (int signum, SigContext* sigContext) {

    // check whether there is a pending signal of the same kind
    if (records[signum].scheduled) {
        delete sigContext;
        return;
    }
    // too many SIGALRMs otherwise
    if (signum == SIGALRM && (records[signum].occurances % signal_alarm_frequency) != 1) {
        delete sigContext;
        return;
    }

    records[signum].sigContext = sigContext;
    records[signum].scheduled = true;
}

void SigManager::scheduleThreadSignal (int procID, int signum, SigContext* sigContext) {
    signalLog->d("[SigManager]scheduleThreadSignal() scheduling signal SIG%s for cpu[%d]\n",
                 getSigName(signum), procID);
    // check whether there is a pending signal of the same kind
    if (procRecords[procID].scheduled) {
        signalLog->d("[SigManager] scheduleThreadSignal() SIG%s is already scheduled\n",
                     getSigName(signum));
        delete sigContext;
        return;
    }

    procRecords[procID].sigContext = sigContext;
    procRecords[procID].scheduled = true;
    numScheduledProcSignal++;
}

// called to run signal handlers for asynchronous signals.
// this code is designed in the interest of multithread-safeness
void SigManager::runScheduledSignal () {
    Processor* P = global_sim->getProcForSignal();
    if (P == NULL) {
        return; // no available processor to handle asynchronous signals
    }

    for (int i=0;i<numRegisteredHandlers;i++) {
        schedulingHead++;
        if (schedulingHead == numRegisteredHandlers) {
            schedulingHead = 0;
        }

        int signum = regd_signum[schedulingHead];
        // check whether a signal is scheduled to be fired
        if (records[signum].scheduled) {
            signalLog->i("[SigManager] runScheduledSignal for SIG%s\n",
                         getSigName(signum));
            records[signum].scheduled = false;
            P->getSigHandler()->trampolineToSigHandler(
                records[signum].sigContext);
            // block more of asynchronous signal while we are running handler
            P->getSigHandler()->blockSignal(records[signum].sigContext);
            records[signum].scheduled = false; // TODO: why is this set again?
            records[signum].sigContext = NULL;
            return;
        }
    }
}

void SigManager::runScheduledThreadSignal () {
    if (numScheduledProcSignal == 0) {
        return;
    }

    for (uint i=0; i<global_sim->cfg.num_cpus; i++) {
        procSchedulingHead++;
        if ((uint)procSchedulingHead == global_sim->cfg.num_cpus) {
            procSchedulingHead = 0;
        }

        if (procRecords[procSchedulingHead].scheduled) {
            int signum = procRecords[procSchedulingHead].sigContext->signum;
            signalLog->i("[SigManager] runScheduledThreadSignal for CPU[%d], SIG%s\n",
                         procSchedulingHead, getSigName(signum));

            Processor* P = global_sim->getProcForThreadSignal(procSchedulingHead);
            
            if (P == NULL) {
                signalLog->d("[SigManager] runScheduledThreadSignal() "
                             "CPU[%d] is already handling signal or is halted\n",
                             procSchedulingHead);
                delete (procRecords[procSchedulingHead].sigContext);
                procRecords[procSchedulingHead].scheduled = false;
                procRecords[procSchedulingHead].sigContext = NULL;
                numScheduledProcSignal--;
                continue; // what should we do?
            } else {
                P->hookScheduleThreadSignal(procSchedulingHead,
                                            signum,
                                            procRecords[procSchedulingHead].sigContext);
            }


            procRecords[procSchedulingHead].scheduled = false;
            numScheduledProcSignal--;
            signalLog->i("[SigManager] runScheduledThreadSignal() running signal for" 
                         "CPU[%d] SIG%s\n", procSchedulingHead, getSigName(signum));
            P->getSigHandler()->
                trampolineToSigHandler(procRecords[procSchedulingHead].sigContext);

            procRecords[procSchedulingHead].scheduled = false;
            procRecords[procSchedulingHead].sigContext = NULL;
            return;
        }
    }
}

// called by TrapGenerator when a trap instruction is executed
void SigManager::invokeSignalHandler (int signum,
                                      siginfo_t* siginfo,
                                      ucontext_t* sigcontext)
{
    signalHandler(signum, siginfo, sigcontext);
}

// called by signalHandler() below
bool SigManager::isSynchronous (int signum) {
    // let's use jikes information directly
    switch (signum) {
        case SIGBUS:
        case SIGSEGV:
        case SIGTRAP:
        case SIGILL:
            return true; // trap handler, SIGPIPE is ignored since jikes set SIG_IGN as sa_handler value
    }
    return false; // software signal handler : SIGALRM, SIGHUP, SIGQUIT, SIGTERM
}

void SigManager::invokeSignalHandler (int signum, Processor* P, void* extra, size_t extraSize) {
    signalLog->i("[SigManager] handler for SIG%s(%d) invoked on processor %d with extra=%p extraSize=%d.\n",
                 getSigName(signum), signum, P->getID(), extra, extraSize);
    P->clearRepeater();
    P->getSigHandler()->trampolineToSigHandler(NEW SigContext(signum, extra, extraSize));

    // austenmc: took these out after conferring w/ jcasper b/c this func is
    // used for simulator-triggered signals so we probably want to continue
    // what we're doing in the simulator...
    //    siglongjmp(sigjmpEnv, signum);
    //    NOTREACHED();
}

// ----------------------------------------------------------------------
// this function is registered to OS to catch all signals coming to the simulator process.
// ----------------------------------------------------------------------


void signalHandler (int signum, siginfo_t* siginfo, ucontext_t* sigcontext) {
    SigManager* sigManager = global_sim->getSM();
    sigManager->checkSignal(signum);
    if (signum != SIGALRM) {
        signalLog->i("%d:%lli:%llx [SigManager] received signal SIG%s\n",
                     curPE != NULL ? (int)curPE->P->getID() : -1,
                     global_sim->getSimclock(),
                     curPE != NULL ? (Reg_s)curPE->getPC() : -1,
                     getSigName(signum));
    }

    // decide whethere this is synchronous or asynchronous
    if (SigManager::isSynchronous(signum)) {
        if (signum == SIGSEGV) {
            Log->w("%d:%lld: [SigManager] SIG%s, real IP=0x%08lx, simulated IP=0x%08x, addr=%p\n",
                   curPE != NULL ? (int)curPE->P->getID() : -1,
                   global_sim->getSimclock(),
                   getSigName(signum),
                   SigContext::getSigIP(sigcontext),
                   curPE != NULL ? (int)ADDR_UINT32(curPE->getPC()) : -1,
                   siginfo->si_addr);
            if (curPE == NULL) {
                Log->printStackTrace(SEV_WARNING);
            }
        }

        // it is a synchronous signal. deal with it now.
        global_sim->noteSignal(NEW SigContext(siginfo, sigcontext), NULL);

        signalLog->i("[SigManager] handler for SIG%s is done. synchronous signal received.\n", getSigName(signum));

        siglongjmp(sigjmpEnv, signum);  // go to sigsetjmp in Simulator::run()
        NOTREACHED();
    }

    // asynchronous case is scheduled to run later
    sigManager->scheduleSignal(signum, NEW SigContext(siginfo, sigcontext));

    // it is an asynchronous signal. schedule it.
    signalLog->i("[SigManager] handler for SIG%s is done. asynchronous signal scheduled.\n", getSigName(signum));
}

// ----------------------------------------------------------------------
// this function is registered to OS to catch sigsegv due to the simulator bugs
// this is a barebone version. it will be beneficial to improve this for better debugging.
// ----------------------------------------------------------------------


void defaultSigSegvHandler (int signum,
                            siginfo_t* siginfo,
                            ucontext_t* sigcontext)
{
    ASSERT(signum == SIGSEGV);

    // if user caused problem via gdb, ignore
    if (global_sim->getGDB()->getInteractive()) {
        siglongjmp(gdbSIGSEGVJumpBuffer, 1);
    }

    if (curPE != NULL) {
        Log->w("[defaultSigSegvHandler] SIG%s curPE ID=%d PC="RegFormat"\n",
               getSigName(signum),
               curPE->getID(),
               curPE->getPC());
    }
    Log->e("[defaultSigSegvHandler] SIG%s IP=0x%08lx addr=%p\n",
           getSigName(signum),
           SigContext::getSigIP(sigcontext),
           siginfo->si_addr);
}

// this function is called from Processor::sendWordAccess
void noop_uint (unsigned addr) {};
